/*************************************************************************
	> File Name: Protocol.hpp
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/10/15 15:36
 ************************************************************************/

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include <jsoncpp/json/json.h>

namespace halil
{
    #define SPACE " "
    #define SPACE_LEN strlen(SPACE)
    #define SEP "\r\n"
    #define SEP_LEN strlen(SEP)

    class Request
    {
    public:
        Request () {}
        Request (int x, int y, char op):_x(x), _y(y), _op(op) {}
        std::string Serialize() const
        {
//#ifdef MYSELF
            std::string str;
            str = std::to_string(_x);
            str += SPACE;
            str += _op;
            str += SPACE;
            str += std::to_string(_y);
            return str;
//#else
            //Json::Value root;
            //root["x"] = _x;
            //root["y"] = _y;
            //root["op"] = _op;
            //Json::FastWriter writer;
            //return writer.write(root);
//#endif
        };

        bool Deserialized(const std::string& str)
        {
//#ifdef MYSELF
            size_t left = str.find(SPACE);
            if (left == std::string::npos) {
                return false;
            }
            size_t right = str.rfind(SPACE);
            if (right == std::string::npos) {
                return false;
            }
            _x = atoi(str.substr(0, left).c_str());
            _y = atoi(str.substr(right + SPACE_LEN).c_str());
            if (left + SPACE_LEN > str.size()) {
                return false;
            } else {
                _op = str[left + SPACE_LEN];
            }
//#else
            //Json::Value root;
            //Json::Reader reader;
            //reader.parse(str, root);
            //_x = root["x"].asInt();
            //_y = root["y"].asInt();
            //_op = root["ip"].asInt();
//#endif
            return true;
        } 

        int _x;
        int _y;
        char _op;
    };

    class Response
    {
    public:

        std::string Serialize() const
        {
//#ifdef MYSELF
            std::string s;
            s = std::to_string(_code);
            s += SPACE;
            s += std::to_string(_result);
            return s;
//#else
            //Json::Value root;
            //root["code"] = _code;
            //root["result"] = _result;
            //root["xx"] = _x;
            //root["yy"] = _y;
            //root["zz"] = _op;
            //Json::FastWriter writer;
            //return writer.write(root);
//#endif
        }

        bool Deserialized(const std::string& s)
        {
//#ifdef MYSELF
            size_t pos = s.find(SPACE);
            if (pos == std::string::npos) {
                return false;
            }
            _code = atoi(s.substr(0, pos).c_str());
            _result = atoi(s.substr(pos + SPACE_LEN).c_str());
//#else
            //Json::Value root;
            //Json::Reader reader;
            //reader.parse(s, root);
            //_code = root["code"].asInt();
            //_result = root["result"].asInt();
            //_x = root["xx"].asInt();
            //_y = root["yy"].asInt();
            //_op = root["zz"].asInt();
//#endif
            return true;
        }

        Response () {}

        Response (int result, int code, int x, int y, char op)
            :_result(result)
            ,_code(code)
            ,_x(x)
            ,_y(y)
            ,_op(op)
        {}
    
        int _result;
        int _code;
        int _x;
        int _y;
        char _op;
    };

    std::string Decode(std::string& buffer) 
    {
        size_t pos = buffer.find(SEP);
        if (pos == std::string::npos) {
            return "";
        }
        int size = atoi(buffer.substr(0, pos).c_str());
        int surplus = buffer.size() - pos - 2 * SEP_LEN;
        if (surplus >= size) {
            buffer.erase(0, pos + SEP_LEN);
            std::string s = buffer.substr(0, size);
            buffer.erase(0, size + SEP_LEN);
            return s;
        } else {
            return "";
        }
    }

    bool Recv(const int sock, std::string* out) 
    {
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof buffer - 1, 0);
        if (s > 0) {
            buffer[s] = 0;
            *out += buffer;
        } else {
            return false;
        }
        return true;
    }

    std::string Encode(const std::string& s) 
    {
        std::string new_package = std::to_string(s.size());
        new_package += SEP;
        new_package += s;
        new_package += SEP;
        return new_package;
    }

    void Send(const int sock, const std::string& str)
    {
        int n = send(sock, str.c_str(), str.size(), 0);
        if (n < 0) {
            logMessage(ERROR, "send error, %d:%s", errno, strerror(errno));
        }
    }
}
